package com.xunwulian.base.util;

import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;

/**
 * XML、Bean、Collection、File之间的转换
 */
public class BeanUtil {

    // 默认的字符编码
    private static final String CHAR_SET = "utf8";

    // 默认的id判断，一个bean必须有int id属性才可以安全地转换
    private static final String ATTR_ID = "id";

    // id=-1的时候表示null，反之，null对象在xml里表示为id=-1的字串
    private static final int NULL_ID = -1;

    // 文件工具
    private FileUtil fileUtil = new FileUtil();

    // 日志工具
    private MyLogUtil myLogUtil = new MyLogUtil(BeanUtil.class);


    /*#######################xml转换##############################*/

    /**
     * 获取root节点名字
     *
     * @return root名字
     */
    private String getRootName(Class<?> type) {
        String root_name = getElemName(type) + "s";
        return root_name;
    }

    /**
     * 获取bean节点名字
     *
     * @return bean名字
     */
    private String getElemName(Class<?> type) {
        String elem_name = "_" + type.getSimpleName().toLowerCase();
        return elem_name;
    }

    /**
     * 获取attribute节点名字
     *
     * @return attribute名字
     */
    private String getAttrName(Class<?> type, Field field) {
        String attr_name = type.getSimpleName().toLowerCase() + "_" + field.getName();
        return attr_name;
    }

    /**
     * attribute节点的名字转换为field的名字
     *
     * @param type      class
     * @param attr_name 节点名
     * @return field名
     */
    private String getAttrName(Class<?> type, String attr_name) {
        return attr_name.replaceFirst(type.getSimpleName().toLowerCase() + "_", "");
    }

    /**
     * 获取class的所有属性域的域名
     *
     * @param type class
     * @return 域名集合
     */
    private List<String> getAttrNames(Class<?> type) {
        List<Field> fields = listAttrField(type);
        return fields.stream().map(p -> getAttrName(type, p)).collect(Collectors.toList());
    }

    /**
     * 获取attribute节点的值
     *
     * @return attribute的值的字串形式
     */
    private String getAttrValue(Field field, Object elem) {

        String value = "";

        try {
            Class<?> type = field.getType();

            // 如果是八种基本类型
            if (isByte(type) || isShort(type) || isInteger(type) || isLong(type) || isFloat(type) || isDouble(type) || isBoolean(type) || isString(type)) {
                if (field.get(elem) != null) {
                    value = field.get(elem).toString();
                }
            } else if (isDate(type) && field.get(elem) != null) {
                Date time = (Date) field.get(elem);
                value = time.getTime() + "";
            } else if (isExternal(type)) {
                if (field.get(elem) != null) {
                    Field attr_id = type.getDeclaredField(ATTR_ID);
                    attr_id.setAccessible(true);

                    value = attr_id.get(field.get(elem)).toString();
                } else {
                    value = NULL_ID + "";
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return value;
    }

    /**
     * 设置attribute值
     */
    private void setAttrValue(Object elem, String fn, String fv) {

        if (elem == null || StringUtils.isBlank(fn) || StringUtils.isBlank(fv)) {
            return;
        }

        Field f = findAttrField(elem.getClass(), fn);
        if (f == null) {
            return;
        }
        f.setAccessible(true);

        Class<?> type = f.getType();
        try {
            if (isByte(type)) {
                f.set(elem, Byte.parseByte(fv));
            } else if (isShort(type)) {
                f.set(elem, Short.parseShort(fv));
            } else if (isInteger(type)) {
                f.set(elem, Integer.parseInt(fv));
            } else if (isLong(type)) {
                f.set(elem, Long.parseLong(fv));
            } else if (isFloat(type)) {
                f.set(elem, Float.parseFloat(fv));
            } else if (isDouble(type)) {
                f.set(elem, Double.parseDouble(fv));
            } else if (isBoolean(type)) {
                f.set(elem, Boolean.parseBoolean(fv));
            } else if (isString(type)) {
                f.set(elem, fv);
            } else if (isDate(type)) {
                f.set(elem, new Date(Long.parseLong(fv)));
            } else if (isExternal(type)) {
                int external_id = Integer.parseInt(fv);
                if (external_id != NULL_ID) {
                    Object external = type.newInstance();

                    Field attr_id = type.getDeclaredField(ATTR_ID);
                    attr_id.setAccessible(true);
                    attr_id.set(external, external_id);

                    f.set(elem, external);
                } else {
                    f.set(elem, null);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 将list解析为xml字串
     *
     * @return 解析出的字串
     */
    public String parseList(List<?> list, Class<?> type) {

        if (type == null) {
            return null;
        }

        if (list == null) {
            list = new ArrayList();
        }

        Document document = DocumentHelper.createDocument();
        document.setXMLEncoding(CHAR_SET);
        Element root = document.addElement(getRootName(type));

        List<Field> fields = listAttrField(type);
        for (Object elem : list) {

            Element child = root.addElement(getElemName(type));

            if (elem == null) {
                try {
                    elem = type.newInstance();
                    Field attr_id = type.getDeclaredField(ATTR_ID);
                    attr_id.setAccessible(true);
                    attr_id.setInt(elem, NULL_ID);
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }
            }

            for (Field field : fields) {
                int mod = field.getModifiers();
                if (isEditable(mod)) {
                    field.setAccessible(true);
                    String attr_name = getAttrName(type, field);
                    String attr_value = getAttrValue(field, elem);
                    child.addElement(attr_name).setText(attr_value);
                }
            }
        }

        return document.asXML();
    }

    /**
     * 将xml字串解析为list
     *
     * @return 解析出的list
     */
    public <T> List<T> parseXML(String content, Class<T> type) {

        List<T> elements = new ArrayList();

        if (type == null || StringUtils.isBlank(content)) {
            return elements;
        }

        try {
            Document document = DocumentHelper.parseText(content);
            Element rootElement = document.getRootElement();

            List<String> attr_names = listAttrField(type).stream().map(p -> getAttrName(type, p)).collect(Collectors.toList());
            Iterator<Element> childs = rootElement.elements().iterator();
            while (childs.hasNext()) {
                T element = type.newInstance();
                Element child = childs.next();

                for (String attr_name : attr_names) {
                    Element param = child.element(attr_name);
                    if (param != null) {
                        String attr_value = param.getText();
                        setAttrValue(element, getAttrName(type, attr_name), attr_value);
                    }
                }

                Field field_id = type.getDeclaredField(ATTR_ID);
                field_id.setAccessible(true);
                if (field_id.getInt(element) == NULL_ID) {
                    element = null;
                }

                elements.add(element);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return elements;
    }

    /**
     * 读取文件内容转换为list
     *
     * @param path 文件的路径
     * @return 转换出的list
     */
    public <T> List<T> readListByFile(String path, Class<T> type) {
        String content = fileUtil.readFile(path);
        return parseXML(content, type);
    }

    /**
     * 将list写入文件
     *
     * @param path 文件的路径
     */
    public void writeFileByList(String path, List<?> list, Class<?> type) {
        String content = parseList(list, type);
        fileUtil.writeFile(path, content);
    }

    /**
     * 将list添加到文件里
     *
     * @param path 文件的路径
     */
    public <T> void addElementsByList(String path, List<T> list, Class<T> type) {
        List<T> list2 = readListByFile(path, type);
        list2.addAll(list);
        writeFileByList(path, list2, type);
    }

    /**
     * 将文件里的list删除
     *
     * @param path 文件的路径
     * @param list 要删除的list
     */
    public <T> void deleteElementsByList(String path, List<T> list, Class<T> type) {
        List<T> list2 = readListByFile(path, type);
        list2.removeAll(list);
        writeFileByList(path, list2, type);
    }


    /*####################### new method ##############################*/

    /**
     * 将bean转换为map，与old methods 里转换后的格式不同。
     * 下面的都是如此
     *
     * @param elem 要转换的bean
     */
    public Map<String, String> beanToMap(Object elem) {

        Map<String, String> map = new HashMap();

        if (elem == null) {
            return map;
        }

        List<Field> fields = listAttrField(elem.getClass());
        for (Field field : fields) {
            int mod = field.getModifiers();
            if (isEditable(mod)) {
                field.setAccessible(true);
                String attr_name = field.getName();
                String attr_value = getAttrValue(field, elem);
                map.put(attr_name, attr_value);
            }
        }

        return map;
    }

    /**
     * 将map转换为bean
     *
     * @param map  要转换的map
     * @param type 转换成的对象的类型
     */
    public <T> T mapToBean(Map<String, String> map, Class<T> type) {
        T elem;
        try {
            elem = type.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        for (String fn : map.keySet()) {
            String fv = map.get(fn);
            setAttrValue(elem, fn, fv);
        }

        return elem;
    }

    private <T> List<String> getMapAttrNames(Class<T> type) {
        List<Field> fields = listAttrField(type);

        return fields.stream().map(p -> p.getName()).collect(Collectors.toList());
    }

    /**
     * xml格式打印
     *
     * @param xml 要打印的xml
     */
    public void log(String xml) {
        Document document;
        try {
            document = DocumentHelper.parseText(xml);
        } catch (Exception e) {
            myLogUtil.log("xml错误，请检查您的xml内容");
            e.printStackTrace();
            return;
        }

        OutputFormat of = OutputFormat.createPrettyPrint();
        try {
            XMLWriter xmlw = new XMLWriter(System.out, of);
            xmlw.write(document);
            xmlw.write("\n");
            xmlw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 是否为byte类型
    private boolean isByte(Class<?> type) {
        return type.equals(Byte.class) || type.equals(byte.class);
    }

    // 是否为short类型
    private boolean isShort(Class<?> type) {
        return type.equals(Short.class) || type.equals(Short.class);
    }

    // 是否为int类型
    private boolean isInteger(Class<?> type) {
        return type.equals(Integer.class) || type.equals(int.class);
    }

    // 是否为long类型
    private boolean isLong(Class<?> type) {
        return type.equals(Long.class) || type.equals(long.class);
    }

    // 是否为float类型
    private boolean isFloat(Class<?> type) {
        return type.equals(Float.class) || type.equals(float.class);
    }

    // 是否为double类型
    private boolean isDouble(Class<?> type) {
        return type.equals(Double.class) || type.equals(double.class);
    }

    // 是否为boolean类型
    private boolean isBoolean(Class<?> type) {
        return type.equals(Boolean.class) || type.equals(boolean.class);
    }

    // 是否为String类型
    private boolean isString(Class<?> type) {
        return type.equals(String.class);
    }

    // 是否为Date类型
    private boolean isDate(Class<?> type) {
        return type.equals(Date.class);
    }

    // 是否为外键
    private boolean isExternal(Class<?> type) {
        try {
            type.getDeclaredField(ATTR_ID);
        } catch (NoSuchFieldException e) {
            return false;
        }

        return true;
    }

    /**
     * Field的修饰符是否是可以编辑的
     *
     * @param mod 修饰符
     * @return 是否可编辑
     */
    private boolean isEditable(int mod) {
        // final与static修饰的field不可编辑
        return !(Modifier.isFinal(mod) || Modifier.isStatic(mod));
    }


    /*#######################高科技##############################*/

    /**
     * 获取某个类型的所有属性域（包含父类型的属性域）
     *
     * @param c 某个类型
     * @return 属性域的集合
     */
    private List<Field> listAttrField(Class<?> c) {

        // 构造一个域的集合
        List<Field> fields = new ArrayList<>();

        // 如果传入的类型为null或者为Object.class，则直接返回
        if (c == null || c == Object.class) {
            return fields;
        }

        // 获得传入的类型的属性域
        Field[] fs = c.getDeclaredFields();
        // 添加获取到的属性域
        fields.addAll(Arrays.asList(fs));

        // 得到父类的Class
        Class pc = c.getSuperclass();
        fields.addAll(listAttrField(pc));

        return fields;
    }

    /**
     * 获取某个域所在的类型
     *
     * @param field 域
     * @param c     类型
     * @param <T>   类型限定
     * @return 真正的类型
     */
    private <T> Class<? super T> findAttrFieldAffiliation(Field field, Class<T> c) {
        Class<? super T> pc = c;
        while (pc != Object.class) {
            if (fieldIsInClass(field, pc)) {
                return pc;
            } else {
                pc = pc.getSuperclass();
            }
        }

        return null;
    }

    /**
     * 某个域是否在这个类型里头
     *
     * @param field 域
     * @param c     类型
     * @param <T>   类型限定
     * @return 是否存在
     */
    private <T> boolean fieldIsInClass(Field field, Class<T> c) {
        if (Arrays.asList(c.getDeclaredFields()).contains(field)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 从类型里获取域名所在的域
     *
     * @param c  类型
     * @param fn 域名
     * @return 域
     */
    private Field findAttrField(Class<?> c, String fn) {

        if (c == null || StringUtils.isBlank(fn)) {
            return null;
        }

        while (c != Object.class) {
            Field[] fs = c.getDeclaredFields();
            for (Field f : fs) {
                if (f.getName().equals(fn)) {
                    return f;
                }
            }

            c = c.getSuperclass();
        }

        return null;
    }
}
